package com.ruoyi.gis.utils;

import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.file.FileUtils;
import it.geosolutions.geoserver.rest.GeoServerRESTPublisher;
import org.geotools.coverage.grid.GridCoverage2D;
import org.geotools.coverage.grid.io.AbstractGridFormat;
import org.geotools.coverage.grid.io.OverviewPolicy;
import org.geotools.coverage.processing.CoverageProcessor;
import org.geotools.data.*;
import org.geotools.data.collection.ListFeatureCollection;
import org.geotools.data.shapefile.ShapefileDataStore;
import org.geotools.data.shapefile.ShapefileDataStoreFactory;
import org.geotools.data.shapefile.dbf.DbaseFileHeader;
import org.geotools.data.shapefile.dbf.DbaseFileReader;
import org.geotools.data.shapefile.files.ShpFiles;
import org.geotools.data.simple.SimpleFeatureCollection;
import org.geotools.data.simple.SimpleFeatureIterator;
import org.geotools.data.simple.SimpleFeatureSource;
import org.geotools.data.simple.SimpleFeatureStore;
import org.geotools.feature.FeatureCollection;
import org.geotools.feature.FeatureIterator;
import org.geotools.feature.NameImpl;
import org.geotools.feature.simple.SimpleFeatureTypeImpl;
import org.geotools.feature.type.GeometryDescriptorImpl;
import org.geotools.feature.type.GeometryTypeImpl;
import org.geotools.gce.geotiff.GeoTiffReader;
import org.geotools.geojson.feature.FeatureJSON;
import org.geotools.geojson.geom.GeometryJSON;
import org.geotools.geometry.GeneralEnvelope;
import org.geotools.geometry.jts.JTS;
import org.geotools.geometry.jts.JTSFactoryFinder;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.geotools.referencing.CRS;
import org.geotools.referencing.NamedIdentifier;
import org.geotools.referencing.crs.DefaultGeographicCRS;
import org.locationtech.jts.geom.Envelope;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.GeometryCollection;
import org.locationtech.jts.geom.GeometryFactory;
import org.locationtech.jts.io.ParseException;
import org.locationtech.jts.io.WKTReader;
//import org.locationtech.jts.io.geojson.GeoJsonReader;
//import org.locationtech.jts.io.twkb.TWKBReader;
import org.locationtech.jts.io.WKTWriter;
import org.opengis.coverage.Coverage;
import org.opengis.coverage.grid.GridEnvelope;
import org.opengis.feature.Feature;
import org.opengis.feature.GeometryAttribute;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.feature.type.AttributeDescriptor;
import org.opengis.feature.type.AttributeType;
import org.opengis.feature.type.GeometryDescriptor;
import org.opengis.feature.type.GeometryType;
import org.opengis.geometry.MismatchedDimensionException;
import org.opengis.parameter.GeneralParameterValue;
import org.opengis.parameter.ParameterValue;
import org.opengis.parameter.ParameterValueGroup;
import org.opengis.referencing.FactoryException;
import org.opengis.referencing.crs.CRSAuthorityFactory;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.crs.SingleCRS;
import org.opengis.referencing.operation.MathTransform;
import org.opengis.referencing.operation.TransformException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.media.jai.ImageLayout;
import java.awt.color.ColorSpace;
import java.awt.image.ColorModel;
import java.awt.image.SampleModel;
import java.io.*;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.*;
import java.util.stream.Collectors;


/**
 * 教材：
 * https://blog.csdn.net/qq_43259860/article/details/124451382
 * geotools简介:geotools和geoserver的关系：
 * https://blog.csdn.net/nmj2008/article/details/113869086
 */
public class GeoUtils {
    public static final Logger logger = LoggerFactory.getLogger(GeoUtils.class);

    /**
     * https://juejin.cn/post/6955459228324593701
     * GeoJson to Shp
     *
     * @param geojsonPath geojson 文件路径
     * @param shpPath     shp 文件路径
     * @return 转换是否成功
     */
    public static boolean transformGeoJsonToShp(String geojsonPath, String shpPath) {
        try {
            // open geojson
            InputStream in = new FileInputStream(geojsonPath);
            GeometryJSON gjson = new GeometryJSON();
            FeatureJSON fjson = new FeatureJSON(gjson);
            FeatureCollection<SimpleFeatureType, SimpleFeature> features = fjson.readFeatureCollection(in);
            // convert schema for shapefile
            SimpleFeatureType schema = features.getSchema();
            GeometryDescriptor geom = schema.getGeometryDescriptor();
            // geojson文件属性
            List<AttributeDescriptor> attributes = schema.getAttributeDescriptors();
            // geojson文件空间类型（必须在第一个）
            GeometryType geomType = null;
            List<AttributeDescriptor> attribs = new ArrayList<>();
            for (AttributeDescriptor attrib : attributes) {
                AttributeType type = attrib.getType();
                if (type instanceof GeometryType) {
                    geomType = (GeometryType) type;
                } else {
                    attribs.add(attrib);
                }
            }
            if (geomType == null)
                return false;

            // 使用geomType创建gt
            GeometryTypeImpl gt = new GeometryTypeImpl(new NameImpl("the_geom"), geomType.getBinding(),
                    geom.getCoordinateReferenceSystem() == null ? DefaultGeographicCRS.WGS84 : geom.getCoordinateReferenceSystem(), // 用户未指定则默认为wgs84
                    geomType.isIdentified(), geomType.isAbstract(), geomType.getRestrictions(),
                    geomType.getSuper(), geomType.getDescription());

            // 创建识别符
            GeometryDescriptor geomDesc = new GeometryDescriptorImpl(gt, new NameImpl("the_geom"), geom.getMinOccurs(),
                    geom.getMaxOccurs(), geom.isNillable(), geom.getDefaultValue());

            // the_geom 属性必须在第一个
            attribs.add(0, geomDesc);

            SimpleFeatureType outSchema = new SimpleFeatureTypeImpl(schema.getName(), attribs, geomDesc, schema.isAbstract(),
                    schema.getRestrictions(), schema.getSuper(), schema.getDescription());
            List<SimpleFeature> outFeatures = new ArrayList<>();
            try (FeatureIterator<SimpleFeature> features2 = features.features()) {
                while (features2.hasNext()) {
                    SimpleFeature f = features2.next();
                    SimpleFeature reType = DataUtilities.reType(outSchema, f, true);

                    reType.setAttribute(outSchema.getGeometryDescriptor().getName(),
                            f.getAttribute(schema.getGeometryDescriptor().getName()));

                    outFeatures.add(reType);
                }
            }
            return saveFeaturesToShp(outFeatures, outSchema, shpPath);
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 保存features为shp格式
     *
     * @param features 要素类
     * @param TYPE     要素类型
     * @param shpPath  shp保存路径
     * @return 是否保存成功
     */
    public static boolean saveFeaturesToShp(List<SimpleFeature> features, SimpleFeatureType TYPE, String shpPath) {
        try {
            ShapefileDataStoreFactory dataStoreFactory = new ShapefileDataStoreFactory();
            File shpFile = new File(shpPath);
            Map<String, Serializable> params = new HashMap<>();
            params.put("url", shpFile.toURI().toURL());
            params.put("create spatial index", Boolean.TRUE);

            ShapefileDataStore newDataStore =
                    (ShapefileDataStore) dataStoreFactory.createNewDataStore(params);
            newDataStore.setCharset(StandardCharsets.UTF_8);

            newDataStore.createSchema(TYPE);

            Transaction transaction = new DefaultTransaction("create");
            String typeName = newDataStore.getTypeNames()[0];
            SimpleFeatureSource featureSource = newDataStore.getFeatureSource(typeName);

            if (featureSource instanceof SimpleFeatureStore) {
                SimpleFeatureStore featureStore = (SimpleFeatureStore) featureSource;
                SimpleFeatureCollection collection = new ListFeatureCollection(TYPE, features);
                featureStore.setTransaction(transaction);
                try {
                    featureStore.addFeatures(collection);
                    //FileUtils.generateCpgFile(shpPath, StandardCharsets.UTF_8);
                    transaction.commit();
                } catch (Exception problem) {
                    problem.printStackTrace();
                    transaction.rollback();
                } finally {
                    transaction.close();
                }
            } else {
                System.out.println(typeName + " does not support read/write access");
            }
        } catch (IOException e) {
            return false;
        }
        return true;
    }


    //https://blog.csdn.net/m0_67920613/article/details/141032533
    public static String shpToGeoGson(File shpFile) throws IOException {
        // Read the shapefile
        FileDataStore store = FileDataStoreFinder.getDataStore(shpFile);
        SimpleFeatureSource featureSource = store.getFeatureSource();
        SimpleFeatureCollection featureCollection = featureSource.getFeatures();
        // Convert features to GeoJSON
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        FeatureJSON featureJSON = new FeatureJSON();
        try (SimpleFeatureIterator features = featureCollection.features()) {
//            featureJSON.writeFeature(features.next(), outputStream);
            featureJSON.writeFeatureCollection(featureCollection, outputStream);
            return outputStream.toString();
        } finally {
            // Clean up temporary files
            store.dispose();
            //FileUtils.deleteDirectory(tempDir);
        }
    }

    public static boolean transformShpToGeoJson(String shpPath, String geojsonPath) {
        try {
            File file = new File(shpPath);
            FileDataStore myData = FileDataStoreFinder.getDataStore(file);
            // 设置解码方式
            ((ShapefileDataStore) myData).setCharset(StandardCharsets.UTF_8);
            SimpleFeatureSource source = myData.getFeatureSource();
            SimpleFeatureType schema = source.getSchema();
            Query query = new Query(schema.getTypeName());
            FeatureCollection<SimpleFeatureType, SimpleFeature> collection = source.getFeatures(query);
            FeatureJSON fjson = new FeatureJSON();
            File geojson = new File(geojsonPath);
            try (FeatureIterator<SimpleFeature> featureIterator = collection.features();
                 StringWriter writer = new StringWriter();
                 BufferedWriter buffer = new BufferedWriter(
                         Files.newBufferedWriter(geojson.toPath(), StandardCharsets.UTF_8))) {
                writer.write("{\"type\":\"FeatureCollection\",\"crs\":");
                fjson.writeCRS(schema.getCoordinateReferenceSystem(), writer);
                writer.write(",");
                writer.write("\"features\":");
                writer.write("[");
                while (featureIterator.hasNext()) {
                    SimpleFeature feature = featureIterator.next();
                    fjson.writeFeature(feature, writer);
                    if (featureIterator.hasNext())
                        writer.write(",");
                }
                writer.write("]");
                writer.write("}");
                buffer.write(writer.toString());
                return true;
            } catch (IOException e) {
                return false;
            }
        } catch (IOException e) {
            return false;
        }
    }
    //https://blog.csdn.net/qq_41613913/article/details/134361460
    public static void ConvertCoordinateReferenceSystem(SimpleFeature feature,
                                                         CoordinateReferenceSystem srcCrs,
                                                         CoordinateReferenceSystem destCrs) {
        try {
            //目标坐标系
//            CRSAuthorityFactory factory = CRS.getAuthorityFactory(true);
//            CoordinateReferenceSystem targetCoordinate =
//            factory.createCoordinateReferenceSystem("EPSG:" + 4490);
            //建立转换
            MathTransform transform = CRS.findMathTransform(srcCrs, destCrs,true);
            //转换
            Geometry geometry = (Geometry) feature.getDefaultGeometry();
            Geometry res = JTS.transform(geometry, transform);
            feature.setDefaultGeometry(res);
        } catch (FactoryException e) {
            e.printStackTrace();
        } catch (TransformException e) {
            e.printStackTrace();
        }
    }

    public static String shpToWkt(File shpFile) throws IOException {
        // Read the shapefile
        FileDataStore store = FileDataStoreFinder.getDataStore(shpFile);
        SimpleFeatureCollection featureCollection = store.getFeatureSource().getFeatures();
        // Convert features to WKT
        WKTWriter wktWriter = new WKTWriter();
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        String data = null;
        try (SimpleFeatureIterator features = featureCollection.features()) {
            while (features.hasNext()) {
                SimpleFeature feature = features.next();
                String wkt = wktWriter.write((Geometry) feature.getDefaultGeometry());
                outputStream.write((wkt + "\n").getBytes());
            }
            data = outputStream.toString();
        } finally {
            // Clean up temporary files
            store.dispose();
//            FileUtils.deleteDirectory(shpFolder);
        }
        return data;
    }
    //https://blog.csdn.net/qq_35997793/article/details/108474807
    public static String wktToJson(String wkt){
        String json = null;
        try{
            WKTReader reader = new WKTReader();
            Geometry geometry = reader.read(wkt);
            StringWriter writer = new StringWriter();
            //GeometryJSON g = new GeometryJSON();
            GeometryJSON g = new GeometryJSON(20);// 这个参数必须测试，不然精度丢失
            g.write(geometry,writer);
            json = writer.toString();
        }catch(Exception e){
            e.printStackTrace();
        }
        return json;
    }
    public static String getShpCrs(String shpPath) {
        String crsStr = null;
        try {
            File shpFile = new File(shpPath);
            //文件编码：dbf文件的第29字节是编码信息，另外cpg文件就是编码明文
            File dbfFile = new File(shpFile.getParent(), FileUtils.getNameNotSuffix(shpPath) + ".dbf");
            Charset charset = getCharsetByDbf(dbfFile);
            ShapefileDataStore shapefileDataStore = new ShapefileDataStore(shpFile.toURI().toURL());
            // 设置编码,防止属性的中文字符出现乱码
            shapefileDataStore.setCharset(charset);//(Charset.forName("GBK"));//(Charset.forName("UTF-8"));//(charset);//
            // 这个typeNamae不传递，默认是文件名称
            //org/geotools/factory/Hints  把gt-main依赖去掉，他们会自动依赖
            FeatureSource featuresource = shapefileDataStore.
                    getFeatureSource(shapefileDataStore.getTypeNames()[0]);
            // 读取bbox
//            ReferencedEnvelope bbox  =featuresource.getBounds();
            // 读取投影
            CoordinateReferenceSystem crs = featuresource.getSchema()
                    .getCoordinateReferenceSystem();
            SingleCRS horizontalCRS = CRS.getHorizontalCRS(crs);
            if (horizontalCRS == null) {
                horizontalCRS = CRS.getVerticalCRS(crs);
            }
            if (horizontalCRS.getName().getCode().equals("GCS_WGS_1984")) {
                return GeoServerRESTPublisher.DEFAULT_CRS;
            }
            Object o = horizontalCRS.getIdentifiers().toArray()[0];
            crsStr = ((NamedIdentifier) o).toString();
            shapefileDataStore.dispose();
        } catch (IOException e) {
            e.printStackTrace();
            return GeoServerRESTPublisher.DEFAULT_CRS;
        }
        return crsStr;
    }

    /**
     * 矢量数据格式
     * wkt
     * csv
     * wkb
     * geojson
     * shapefile
     *
     * @param shpPath
     */
    //读取shp格式
    public static JSONObject readShpFile(String shpPath) {
        JSONObject data = new JSONObject();
        try {
            logger.info("----------------readShpFile--------------------");
//            ShpFiles shpFiles = new ShpFiles(shpPath);
//            DbaseFileReader dbaseFileHeader = new DbaseFileReader(shpFiles,false,Charset.forName("UTF-8"));
//            DbaseFileHeader header = dbaseFileHeader.getHeader();
            File shpFile = new File(shpPath);
            data.put("fileName", shpFile.getName());
            //文件编码：dbf文件的第29字节是编码信息，另外cpg文件就是编码明文
            Charset charset = getCharsetByCpg(shpFile.getParent(), FileUtils.getNameNotSuffix(shpPath));
            if (charset == null) {
                File dbfFile = new File(shpFile.getParent(), FileUtils.getNameNotSuffix(shpPath) + ".dbf");
                charset = getCharsetByDbf(dbfFile);
                dbfFile = null;
            }
            data.put("charset", charset.toString());
            ShapefileDataStore shapefileDataStore = new ShapefileDataStore(shpFile.toURI().toURL());
            // 设置编码,防止属性的中文字符出现乱码
            shapefileDataStore.setCharset(charset);//(Charset.forName("GBK"));//(Charset.forName("UTF-8"));//(charset);//
            // 这个typeNamae不传递，默认是文件名称
            //org/geotools/factory/Hints  把gt-main依赖去掉，他们会自动依赖
            FeatureSource featuresource = shapefileDataStore.
                    getFeatureSource(shapefileDataStore.getTypeNames()[0]);
            data.put("typeNames", shapefileDataStore.getTypeNames());
            // 读取bbox
            ReferencedEnvelope bbox = featuresource.getBounds();
            JSONObject bound = new JSONObject();
            bound.put("maxX", bbox.getMaxX());
            bound.put("maxY", bbox.getMaxY());
            bound.put("minX", bbox.getMinX());
            bound.put("minY", bbox.getMinY());
            data.put("bbox", bound);
            // 读取投影
            // 读取投影
            CoordinateReferenceSystem crs = featuresource.getSchema()
                    .getCoordinateReferenceSystem();
            data.put("crs", crs.toString());
            SingleCRS horizontalCRS = CRS.getHorizontalCRS(crs);
            if (horizontalCRS == null) {
                horizontalCRS = CRS.getVerticalCRS(crs);
            }
            String crsStr = GeoServerRESTPublisher.DEFAULT_CRS;
            if (horizontalCRS != null && horizontalCRS.getIdentifiers() != null
                    && horizontalCRS.getIdentifiers().size() > 0) {
                Object o = horizontalCRS.getIdentifiers().toArray()[0];
                crsStr = ((NamedIdentifier) o).toString();
            }
            data.put("crsStr", crsStr);
            // 特征总数
            int count = featuresource.getCount(Query.ALL);
            data.put("featureSourceCount", count);
            // 获取当前数据的geometry类型（点、线、面）
            GeometryType geometryType = featuresource.getSchema().getGeometryDescriptor().getType();
            data.put("geometryType", geometryType.getName().toString());
            // 读取要素
            SimpleFeatureCollection simpleFeatureCollection = (SimpleFeatureCollection) featuresource.getFeatures();
            // 获取当前矢量数据有哪些属性字段值
            List<AttributeDescriptor> attributes = simpleFeatureCollection.getSchema().getAttributeDescriptors();
            List<JSONObject> types = attributes.stream().map(item -> {
                JSONObject filed = new JSONObject();
                filed.put("type", item.getType().getBinding().getSimpleName());
                filed.put("localName", item.getName().toString());
                filed.put("defaultValue", item.getDefaultValue());
                return filed;
            }).collect(Collectors.toList());
            data.put("attributes", types);
            //org.locationtech.jts.geom 冲突
            // 拿到迭代器
            SimpleFeatureIterator simpleFeatureIterator = simpleFeatureCollection.features();
            JSONArray attValues = new JSONArray();
            // 遍历每一个要素
            while (simpleFeatureIterator.hasNext()) {
                SimpleFeature simpleFeature = simpleFeatureIterator.next();
                JSONObject itemVal = new JSONObject();
                // java8新特性流api
                attributes.stream().forEach((a) -> {
                    // 依次读取这个shape中每一个属性值，当然这个属性值，可以处理其它业务
//                    System.out.println(a.getLocalName() + ":" + simpleFeature.getAttribute(a.getLocalName()));
                    itemVal.put(a.getLocalName(), simpleFeature.getAttribute(a.getLocalName()).toString());
                });
                attValues.add(itemVal);
            }
            data.put("data", attValues);
            shapefileDataStore.dispose();
            logger.info("----------------readShpFile-----end---------------");
        } catch (IOException e) {
            e.printStackTrace();
        }
        System.out.println("读取完成！");
        return data;
    }

    public static Charset getCharsetByCpg(String parentPath, String fileName) {
        try {
            File file = new File(parentPath, fileName + ".CPG");
            if (!file.exists()) {
                file = new File(parentPath, fileName + ".cpg");
            }
            if (!file.exists()) return null;
            String s = FileUtils.readFileToString(file, Charset.forName("UTF-8"));
            if (StringUtils.isEmpty(s)) return null;
            return Charset.forName(s);
        } catch (Exception e) {
            return null;
        }
    }

    public static Charset getCharsetByCpg(String filePath) {
        Charset charset = null;
        try {
            File file = new File(filePath);
            if (file.exists()) {
                String s = FileUtils.readFileToString(file, Charset.forName("UTF-8"));
                return Charset.forName(s);
            } else {
                return null;
            }
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    //https://www.jianshu.com/p/71b90feb3869
    public static Charset getCharsetByDbf(File file) {
        Charset charset = null;
        InputStream dbf = null;
        try {
            dbf = new FileInputStream(file);
            byte[] bytes = new byte[30];
            dbf.read(bytes);
            byte b = bytes[29];
            String hex = Integer.toHexString(Byte.toUnsignedInt(b));
            if ("4d".equalsIgnoreCase(hex)) {//4d
                return Charset.forName("GBK");
            }
            return Charset.forName("UTF-8");
        } catch (Exception e) {
            e.printStackTrace();
            return Charset.forName("UTF-8");
        } finally {
            if (dbf != null) {
                try {
                    dbf.close();
                    dbf = null;
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    //一个shp文件通常由：shp、dbf、shx、prj四个文件组成，其中prj代表空间参考信息，shx存储了索引信息，
    // 有助于程序加快搜索效率，shp保存了元素的几何实体，最后dbf里面存放了每个几何形状的属性数据，
    // 所以我们将shp文件编码，实际上是讲dbf文件编码，因为只有dbf来说，
    // 才有可能存储GBK编码的中文或其他编码的其他语言的
    //dbf文件的第30个字节代表了编码类型（不是绝对的，不过我试了很多软件，大部分还是遵循这个标准的），
    // 所以我们只需要读取dbf的第30个字节，然后根据一个dbf编码表就能得到这个shp的编码了
    //作者：Scially
    //链接：https://www.jianshu.com/p/71b90feb3869
    //来源：简书
    //著作权归作者所有。商业转载请联系作者获得授权，非商业转载请注明出处。

    /**
     * 解析.dbf
     *
     * @param path
     * @return
     * @throws Exception
     * @author ygc
     */
    public static List<Object> readDbfFile(String path) {
        List<Object> list = new ArrayList<Object>();
        Map<Object, Object> values = null;
        DbaseFileReader reader = null;
        try {
            Charset charset = getCharsetByDbf(new File(path));
            reader = new DbaseFileReader(new ShpFiles(path), false, charset);
            DbaseFileHeader header = reader.getHeader();
            int numFields = header.getNumFields();
            //迭代读取记录
            while (reader.hasNext()) {
                try {
                    Object[] entry = reader.readEntry();
                    values = new LinkedHashMap<Object, Object>();
                    for (int i = 0; i < numFields; i++) {
                        String title = header.getFieldName(i);
                        Object value = entry[i];
                        values.put(title, value);
                        System.out.println(title + "=" + value);
                    }

                } catch (Exception e) {
                    e.printStackTrace();
                }
                list.add(values);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (reader != null) {
                //关闭
                try {
                    reader.close();
                } catch (Exception e) {
                }
            }
        }
        return list;
    }

    public static String getShapeType(String type) {
        String pDbFieldType = "";
        switch (type) {
            case "Integer":
                pDbFieldType = "INTEGER";
                break;
            case "Double":
                pDbFieldType = "DOUBLE";
                break;
            case "String":
                pDbFieldType = "VARCHAR";
                break;
            case "Date":
                pDbFieldType = "DATETIME";
                break;
            case "Boolean":
                pDbFieldType = "BOOLEAN";
                break;
            case "Geometry":
                pDbFieldType = "Geometry";
                break;
            default:
                pDbFieldType = "TEXT";
                break;
        }
        return pDbFieldType;
    }


    //读取wkb格式
    //ex : 0000010300000001000000A4000000B98D06F0163C5E408D28ED0DBE684440B98D06F0163C5E408D28ED0DBE684440B98D06F0163C5E408D28ED0DBE684440B98D06F0163C5E408D28ED0DBE684440075F984C153C5E4029C
//    public static void readWkb() throws ParseException {
//        String tokens = "0000010300000001000000A4000000B98D06F0163C5E408D28ED0DBE684440B98D06F0163C5E408D28ED0DBE684440B98D06F0163C5E408D28ED0DBE684440B98D06F0163C5E408D28ED0DBE684440075F984C153C5E4029C";
//        TWKBReader wkbReader = new TWKBReader();
//        Geometry geometry = wkbReader.read(TWKBReader.hexToBytes(tokens));
//    }
    //读取wkt
    //ex : MultiPolygon ( ((10 10, 10 20, 20 20, 20 15, 10 10)), ((60 60, 70 70, 80 60, 60 60 )) )
    public static void readWkt() throws ParseException, org.geotools.geometry.iso.io.wkt.ParseException {
        String wktStr = "MultiPolygon ( ((10 10, 10 20, 20 20, 20 15, 10 10)), ((60 60, 70 70, 80 60, 60 60 )) )";
        WKTReader wktReader = new WKTReader();
        Geometry geometry = wktReader.read(wktStr);
    }

    //读取geojson
    //ex: {“type”:“Feature”,“crs”:{“type”:“name”,“properties”:{“name”:“EPSG:2380”}},“geometry”:{“type”:“MultiPolygon”,“coordinates”:[[[[646398.9535,3267941.9664],[649558.7196,3267895.3528],[649674.763,3265683.4124],[646387.8773,3265827.4858],[646398.9535,3267941.9664]]]]},“properties”:{“Id”:0}}
    public static void readGeoJson(String jsonPath) throws IOException, ParseException {
        //https://jar-download.com/artifacts/org.locationtech.jts.io/jts-io-common
//        File file = new File(jsonPath);
//        FileInputStream fileInputStream = new FileInputStream(file);
        String jsonStr = FileUtils.readFileToString(new File(jsonPath), Charset.forName("UTF-8"));
        // 这里可能存在问题，如果是超大文件怎么办，一次性读入会不会报内存
        // 解决方案是不是对大文件进行拆分
//        GeoJsonReader geoJSONReader = new GeoJsonReader();
//        Geometry geometry = geoJSONReader.read(jsonStr);
        System.out.println("读取JSON完毕！");
        //或者直接用fastjson读取解析
    }


    //shp 新增要素（创建图层）
    //1.构造TYPE（定义数据的坐标参考、属性字段）
    //Class的取值可以是：几何（Point.class,Polygon.class,MultiPolygon.class），属性（String.class，Integer.class， Double.class）
//    public static SimpleFeatureType createType(Class<?> c, String layerName) throws ParseException, IOException {
//        SimpleFeatureTypeBuilder builder = new SimpleFeatureTypeBuilder();
//        builder.setCRS(DefaultGeographicCRS.WGS84);
//        builder.add("FID",String.class);
//        // 需要注意的是几何字段的属性名称是固定的
//        builder.add("the_geom", c);
//        // 设置了图层的名字
//        builder.setName(layerName);
//        SimpleFeatureType simpleFeatureType = builder.buildFeatureType();
//        //2.根据TYPE构建单个要素
//        SimpleFeatureBuilder featureBuilder = new SimpleFeatureBuilder(simpleFeatureType);
//        WKTReader wktReader = new WKTReader();
//        String wktStr = "";
//        Geometry geometry = wktReader.read(wktStr);
//        // 这里的添加顺序和上面TYPE的时候保持一致
//        featureBuilder.add("1");
//        featureBuilder.add(geometry);
//        SimpleFeature feature = featureBuilder.buildFeature(null);
//        //3.创建FeatureCollection
//        List<SimpleFeature> features = new ArrayList<>();
//        // 只需要将上面步骤的单个要素放入循环中创建更多的要素
//        features.add(feature);
//        SimpleFeatureCollection collection = new ListFeatureCollection(simpleFeatureType, features);
//        // //4.导出为shp
//        // 生成shpfile
//        String shpPath = "";
//        File shpFile = new File(shpPath);
//
//        ShapefileDataStoreFactory dataStoreFactory = new ShapefileDataStoreFactory();
//        // 创造shpstore需要的参数
//        Map<String, Serializable> params = new HashMap<>();
//        params.put("url", shpFile.toURI().toURL());
//        params.put("create spatial index", Boolean.TRUE);
//        ShapefileDataStore newDataStore = (ShapefileDataStore) dataStoreFactory.createNewDataStore(params);
//        newDataStore.createSchema(simpleFeatureType);
//        Transaction transaction = new DefaultTransaction("create");
//        String typeName = newDataStore.getTypeNames()[0];
//        SimpleFeatureSource featureSource = newDataStore.getFeatureSource(typeName);
//        SimpleFeatureStore featureStore = (SimpleFeatureStore) featureSource;
//        SimpleFeatureCollection collection2 = new ListFeatureCollection(simpleFeatureType, features);
//        featureStore.setTransaction(transaction);
//        featureStore.addFeatures(collection2);
//        featureStore.setTransaction(transaction);
//        transaction.commit();
//        transaction.close();
//        return simpleFeatureType;
//    }

    public static SimpleFeatureCollection readFeatureCollection(String shpPath) throws IOException {
        File shpFile = new File(shpPath);
        ShapefileDataStore shapefileDataStore = new ShapefileDataStore(shpFile.toURI().toURL());
        // 设置编码,防止属性的中文字符出现乱码
        shapefileDataStore.setCharset(Charset.forName("UTF-8"));
        // 这个typeNamae不传递，默认是文件名称
        FeatureSource featuresource = shapefileDataStore.getFeatureSource(shapefileDataStore.getTypeNames()[0]);
        // 读取bbox
        ReferencedEnvelope bbox = featuresource.getBounds();
        // 读取投影
        CoordinateReferenceSystem crs = featuresource.getSchema().getCoordinateReferenceSystem();
        // 特征总数
        int count = featuresource.getCount(Query.ALL);
        // 获取当前数据的geometry类型（点、线、面）
        GeometryType geometryType = featuresource.getSchema().getGeometryDescriptor().getType();
        // 读取要素
        SimpleFeatureCollection simpleFeatureCollection = (SimpleFeatureCollection) featuresource.getFeatures();
        return simpleFeatureCollection;
    }

    ////////////////////////////////////////////////////////////////////
    //栅格数据
//    arcgrid
//    geotiff
//    grassraster
//    image ( JPEG TIFF GIF PNG)
//    imageio-ext-gdal
//    imagemosaic
//    imagepyramid
//    JP2K
//    matlab
    //读取TIFF
    public static Coverage readTiff(String tiffPath) throws IOException {
        File f = new File(tiffPath);
        ParameterValue<OverviewPolicy> policy = AbstractGridFormat.OVERVIEW_POLICY.createValue();
        policy.setValue(OverviewPolicy.IGNORE);
        ParameterValue<String> gridsize = AbstractGridFormat.SUGGESTED_TILE_SIZE.createValue();
        ParameterValue<Boolean> useJaiRead = AbstractGridFormat.USE_JAI_IMAGEREAD.createValue();
        useJaiRead.setValue(true);
        GridCoverage2D image = new GeoTiffReader(f).
                read(new GeneralParameterValue[]{policy, gridsize, useJaiRead});
        //image.getCoordinateReferenceSystem()
        return image;
    }

    public void readTiffFile(String tiffPath) throws IOException {
        /**
         * 使用GeoTiffReader
         */
        GeoTiffReader geoTiffReader = new GeoTiffReader(tiffPath);
        //获取到其bbox
        GeneralEnvelope envelope = geoTiffReader.getOriginalEnvelope();
        //获取到投影信息
        CoordinateReferenceSystem crs = geoTiffReader.getCoordinateReferenceSystem();
        //获取tiff的gridrange(网格范围，按像素说就是像素数量，也就是是tiff的rectangle)
        GridEnvelope gridEnvelope = geoTiffReader.getOriginalGridRange();
        //获取其图像相关信息
        ImageLayout imageLayout = geoTiffReader.getImageLayout();
        //获取colormodel
        ColorModel colorModel = imageLayout.getColorModel(null);
        //查看其通道数量
        int num = colorModel.getNumComponents();
        //是否支持透明
        boolean isAlpha = colorModel.hasAlpha();
        //获取颜色空间
        ColorSpace clorspace = colorModel.getColorSpace();
        //获取samplemodel
        SampleModel sampleModel = imageLayout.getSampleModel(null);
        //数据类型
        int datatype = sampleModel.getDataType();
    }

    //异常问题：https://blog.csdn.net/Bazingalqq/article/details/88871611
    //
    public static String readTiffCrs(String tiffPath) {
        String crsStr = null;
        try {
            GeoTiffReader geoTiffReader = new GeoTiffReader(tiffPath);
            //获取到其bbox
//            GeneralEnvelope envelope = geoTiffReader.getOriginalEnvelope();
            //获取到投影信息
            CoordinateReferenceSystem crs = geoTiffReader.getCoordinateReferenceSystem();
            SingleCRS horizontalCRS = CRS.getHorizontalCRS(crs);
            Object o = horizontalCRS.getIdentifiers().toArray()[0];
            crsStr = ((NamedIdentifier) o).toString();
            geoTiffReader.dispose();
        } catch (IOException e) {
            logger.info("get tif crs error " + e.getMessage());
            return GeoServerRESTPublisher.DEFAULT_CRS;
        }
        return crsStr;
    }
    //生成TIFF
//    File file = new File(outTiffPath);
//    GeoTiffWriter geoTiffWriter = new GeoTiffWriter(file);
//    final GeoTiffFormat format = new GeoTiffFormat();
//    final GeoTiffWriteParams wp = new GeoTiffWriteParams();
//    // 设置写出参数
//    wp.setCompressionMode(GeoTiffWriteParams.MODE_DEFAULT);
//    wp.setTilingMode(GeoToolsWriteParams.MODE_DEFAULT);
//        ParameterValueGroup paramWrite = format.getWriteParameters();
//    paramWrite.parameter(AbstractGridFormat.GEOTOOLS_WRITE_PARAMS.getName().toString()).setValue(wp);
//    geoTiffWriter.write((GridCoverage) coverage, paramWrite.values().toArray(new GeneralParameterValue[4]) );
//    geoTiffWriter.dispose();


    //通过掩膜提取
    private static Coverage clipImageToFeatureSource() throws IOException, FactoryException, MismatchedDimensionException, TransformException {
        SimpleFeatureCollection collection = readFeatureCollection("E:\\data\\shp\\mask.shp");
        FeatureIterator<SimpleFeature> iterator = collection.features();
        List<Geometry> all = new ArrayList<Geometry>();
        try {
            while (iterator.hasNext()) {
                SimpleFeature feature = iterator.next();
                Geometry geometry = (Geometry) feature.getDefaultGeometry();
                all.add(geometry);
            }
        } finally {
            if (iterator != null) {
                iterator.close();
            }
        }
        Coverage coverage = readTiff("a.tif");
        Coverage clippedCoverage = null;
        if (all.size() > 0) {
            CoverageProcessor processor = new CoverageProcessor();
            ParameterValueGroup params = processor.getOperation("CoverageCrop")
                    .getParameters();
            params.parameter("Source").setValue(coverage);
            GeometryFactory factory = JTSFactoryFinder.getGeometryFactory(null);
            Geometry[] a = all.toArray(new Geometry[0]);
            GeometryCollection c = new GeometryCollection(a, factory);
            Envelope envelope = all.get(0).getEnvelopeInternal();
            double x1 = envelope.getMinX();
            double y1 = envelope.getMinY();
            double x2 = envelope.getMaxX();
            double y2 = envelope.getMaxY();
            ReferencedEnvelope referencedEnvelope = new ReferencedEnvelope(x1, x2, y1, y2, coverage.getCoordinateReferenceSystem());
            params.parameter("ENVELOPE").setValue(referencedEnvelope);
            params.parameter("ROI").setValue(c);
            params.parameter("ForceMosaic").setValue(true);
            clippedCoverage = processor.doOperation(params);
        }
        if (all.size() == 0) {
            System.out.println("Crop by shapefile requested but no simple features matched extent!");
        }
        return clippedCoverage;
    }


    public static int getSrid(String code) {
        switch (code) {
            //case "CGCS2000_25":
            case "CGCS2000 / 3-degree Gauss-Kruger zone 25":
            case "CGCS2000_3_Degree_GK_Zone_25":
            case "CGCS2000_3_degree_Gauss_Kruger_zone_25":
            case "CGCS_2000_3_Degree_GK_Zone_25": {
                return 4513;
            }
            case "CGCS2000 / 3-degree Gauss-Kruger zone 26":
            case "CGCS2000_3_Degree_GK_Zone_26":
            case "CGCS2000_3_degree_Gauss_Kruger_zone_26":
            case "CGCS_/2000_3_Degree_GK_Zone_26": {
                return 4514;
            }
            case "CGCS2000 / 3-degree Gauss-Kruger zone 27":
            case "CGCS2000_3_Degree_GK_Zone_27":
            case "CGCS2000_3_degree_Gauss_Kruger_zone_27":
            case "CGCS_2000_3_Degree_GK_Zone_27": {
                return 4515;
            }
            case "CGCS2000 / 3-degree Gauss-Kruger zone 28":
            case "CGCS2000_3_Degree_GK_Zone_28":
            case "CGCS2000_3_degree_Gauss_Kruger_zone_28":
            case "CGCS_2000_3_Degree_GK_Zone_28": {
                return 4516;
            }
            case "CGCS2000 / 3-degree Gauss-Kruger zone 29":
            case "CGCS2000_3_Degree_GK_Zone_29":
            case "CGCS2000_3_degree_Gauss_Kruger_zone_29":
            case "CGCS_2000_3_Degree_GK_Zone_29": {
                return 4517;
            }
            case "CGCS2000 / 3-degree Gauss-Kruger zone 30":
            case "CGCS2000_3_Degree_GK_Zone_30":
            case "CGCS2000_3_degree_Gauss_Kruger_zone_30":
            case "CGCS_2000_3_Degree_GK_Zone_30": {
                return 4518;
            }
            case "CGCS2000 / 3-degree Gauss-Kruger zone 31":
            case "CGCS2000_3_Degree_GK_Zone_31":
            case "CGCS2000_3_degree_Gauss_Kruger_zone_31":
            case "CGCS_2000_3_Degree_GK_Zone_31": {
                return 4519;
            }
            case "CGCS2000 / 3-degree Gauss-Kruger zone 32":
            case "CGCS2000_3_Degree_GK_Zone_32":
            case "CGCS2000_3_degree_Gauss_Kruger_zone_32":
            case "CGCS_2000_3_Degree_GK_Zone_32": {
                return 4520;
            }
            case "CGCS2000 / 3-degree Gauss-Kruger zone 33":
            case "CGCS2000_3_Degree_GK_Zone_33":
            case "CGCS2000_3_degree_Gauss_Kruger_zone_33":
            case "CGCS_2000_3_Degree_GK_Zone_33": {
                return 4521;
            }
            case "CGCS2000 / 3-degree Gauss-Kruger zone 34":
            case "CGCS2000_3_Degree_GK_Zone_34":
            case "CGCS2000_3_degree_Gauss_Kruger_zone_34":
            case "CGCS_2000_3_Degree_GK_Zone_34": {
                return 4522;
            }
            case "CGCS2000 / 3-degree Gauss-Kruger zone 35":
            case "CGCS2000_3_Degree_GK_Zone_35":
            case "CGCS2000_3_degree_Gauss_Kruger_zone_35":
            case "CGCS_2000_3_Degree_GK_Zone_35": {
                return 4523;
            }
            case "CGCS2000 / 3-degree Gauss-Kruger zone 36":
            case "CGCS2000_3_Degree_GK_Zone_36":
            case "CGCS2000_3_degree_Gauss_Kruger_zone_36":
            case "CGCS_2000_3_Degree_GK_Zone_36": {
                return 4524;
            }
            case "CGCS2000 / 3-degree Gauss-Kruger zone 37":
            case "CGCS2000_3_Degree_GK_Zone_37":
            case "CGCS2000_3_degree_Gauss_Kruger_zone_37":
            case "CGCS_2000_3_Degree_GK_Zone_37": {
                return 4525;
            }
            case "CGCS2000 / 3-degree Gauss-Kruger zone 38":
            case "CGCS2000_3_Degree_GK_Zone_38":
            case "CGCS2000_3_degree_Gauss_Kruger_zone_38":
            case "CGCS_2000_3_Degree_GK_Zone_38": {
                return 4526;
            }
            case "CGCS2000 / 3-degree Gauss-Kruger zone 39":
            case "CGCS2000_3_Degree_GK_Zone_39":
            case "CGCS2000_3_degree_Gauss_Kruger_zone_39":
            case "CGCS_2000_3_Degree_GK_Zone_39": {
                return 4527;
            }
            case "CGCS2000 / 3-degree Gauss-Kruger zone 40":
            case "CGCS2000_3_Degree_GK_Zone_40":
            case "CGCS2000_3_degree_Gauss_Kruger_zone_40":
            case "CGCS_2000_3_Degree_GK_Zone_40": {
                return 4528;
            }
            case "CGCS2000 / 3-degree Gauss-Kruger zone 41":
            case "CGCS2000_3_Degree_GK_Zone_41":
            case "CGCS2000_3_degree_Gauss_Kruger_zone_41":
            case "CGCS_2000_3_Degree_GK_Zone_41": {
                return 4529;
            }
            case "CGCS2000 / 3-degree Gauss-Kruger zone 42":
            case "CGCS2000_3_Degree_GK_Zone_42":
            case "CGCS2000_3_degree_Gauss_Kruger_zone_42":
            case "CGCS_2000_3_Degree_GK_Zone_42": {
                return 4530;
            }
            case "CGCS2000 / 3-degree Gauss-Kruger zone 43":
            case "CGCS2000_3_Degree_GK_Zone_43":
            case "CGCS2000_3_degree_Gauss_Kruger_zone_43":
            case "CGCS_2000_3_Degree_GK_Zone_43": {
                return 4531;
            }
            case "CGCS2000 / 3-degree Gauss-Kruger zone 44":
            case "CGCS2000_3_Degree_GK_Zone_44":
            case "CGCS2000_3_degree_Gauss_Kruger_zone_44":
            case "CGCS_2000_3_Degree_GK_Zone_44": {
                return 4532;
            }
            case "CGCS2000 / 3-degree Gauss-Kruger zone 45":
            case "CGCS2000_3_Degree_GK_Zone_45":
            case "CGCS2000_3_degree_Gauss_Kruger_zone_45":
            case "CGCS_2000_3_Degree_GK_Zone_45": {
                return 4533;
            }
        }
        return 0;
    }

    //https://blog.csdn.net/qq_39035773/article/details/121122915
    ////////////////////////////////////////////////////////////////////
    public static String geoGsonToWkt(String geoJSON) {
        // 使用GeoJSON解析器解析GeoJSON字符串
        String wkt = null;
        try {
//            FeatureJSON featureJSON = new FeatureJSON();
//            Feature feature = featureJSON.readFeature(new StringReader(geoJSON));
//            // 获取Geometry对象
//            Geometry geometry = (Geometry) feature.getDefaultGeometryProperty().getValue();
//            // 使用WKT格式输出Geometry对象
//            String wkt = geometry.toText();
//            return wkt;
            // 使用 FeatureJSON 来解析 GeoJSON
            FeatureJSON featureJSON = new FeatureJSON();
            FeatureCollection featureCollection = featureJSON.
                    readFeatureCollection(new StringReader(geoJSON));
            // 遍历 FeatureCollection 中的每个 Feature
            try (FeatureIterator iterator = featureCollection.features()) {
                while (iterator.hasNext()) {
                    Feature feature = iterator.next();
                    // 获取 Geometry 对象
                    GeometryAttribute defaultGeometryProperty = feature.getDefaultGeometryProperty();
                    Geometry geometry = (Geometry) defaultGeometryProperty.getValue();
                    // 使用 WKT 格式输出 Geometry 对象
                    wkt = geometry.toText();
                    System.out.println(wkt);
                    if(StringUtils.isEmpty(wkt)){
                        break;//我们只要一个
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return wkt;
    }


    ////////////////////////////////////////////////////////////////////
    public static void main(String[] args) throws IOException {
//        String s = readTiffCrs("D:\\work\\数据\\多光谱数据投影文件\\quanju_noalpha_reflectance_green_1_1.tif");
//        String s = readTiffCrs("E:\\遥感影像\\juegeo2019new.tif");

//        readShpFile("D:\\work\\数据\\csdn全国铁路线+火车站点数据\\铁路数据\\火车站\\火车站.shp");
//        readShpFile("D:\\work\\数据\\高标测试数据\\禹城\\2019\\2019年禹城市梁家镇1万亩高标准农田建设项目\\竣工阶段\\1234376507635044353.shp");
        //GB18030---gbk读取正常
//        readShpFile("D:/data/zhongwen/GBZ2012451030GT西林县八达镇土黄村等3个村土地整理项目YS.shp");
        //GB18030---UTF-读取正常
//        readShpFile("D:\\data\\shp\\2021年灵川县大圩镇、定江镇、潮田乡高标准农田建设项目\\竣工阶段\\1471467801610571777.shp");
//        try {
//            String path1="D:/data/zhongwen/GBZ2012451030GT西林县八达镇土黄村等3个村土地整理项目YS.dbf";
//            String path="D:\\\\data\\\\shp\\\\2021年灵川县大圩镇、定江镇、潮田乡高标准农田建设项目\\\\竣工阶段\\\\1471467801610571777.dbf";
//            List<Object> objects = readDbfFile(path1);
//            List<Object> objects2 = readDbfFile(path);
//            readShpFile(path);
//        } catch (Exception e) {
//            e.printStackTrace();
//        }

//        readTiff("D:\\work\\赤红壤\\数据\\刘园增城市数据\\2020年增城市作物轮作系统\\2020年增城市作物轮作系统.tif");

        String aa = shpToGeoGson(new File("D:\\work\\earthai\\竹山县十堰市_湖北\\竹山县十堰市_湖北.shp"));
        int bb = 0;
    }
}
